﻿using Grand.Business.Core.Interfaces.Marketing.Customers;
using Grand.Data;
using Grand.Domain;
using Grand.Domain.Customers;
using Grand.Infrastructure.Caching;
using Grand.Infrastructure.Caching.Constants;
using Grand.Infrastructure.Extensions;
using MediatR;

namespace Grand.Business.Marketing.Services.Customers;

/// <summary>
///     Customer product service interface
/// </summary>
public class CustomerProductService : ICustomerProductService
{
    private readonly ICacheBase _cacheBase;

    private readonly IRepository<CustomerProductPrice> _customerProductPriceRepository;
    private readonly IRepository<CustomerProduct> _customerProductRepository;
    private readonly IMediator _mediator;

    public CustomerProductService(
        IRepository<CustomerProductPrice> customerProductPriceRepository,
        IRepository<CustomerProduct> customerProductRepository,
        ICacheBase cacheBase,
        IMediator mediator)
    {
        _cacheBase = cacheBase;
        _customerProductPriceRepository = customerProductPriceRepository;
        _customerProductRepository = customerProductRepository;
        _mediator = mediator;
    }

    #region Customer Product Price

    /// <summary>
    ///     Gets a customer product price
    /// </summary>
    /// <param name="id">Identifier</param>
    /// <returns>Customer product price</returns>
    public virtual Task<CustomerProductPrice> GetCustomerProductPriceById(string id)
    {
        return _customerProductPriceRepository.GetByIdAsync(id);
    }

    /// <summary>
    ///     Gets a price
    /// </summary>
    /// <param name="customerId">Customer Identifier</param>
    /// <param name="productId">Product Identifier</param>
    /// <returns>Customer product price</returns>
    public virtual async Task<double?> GetPriceByCustomerProduct(string customerId, string productId)
    {
        var key = string.Format(CacheKey.CUSTOMER_PRODUCT_PRICE_KEY_ID, customerId, productId);
        var productPrice = await _cacheBase.GetAsync(key, async () =>
        {
            var pp = await Task.FromResult(
                _customerProductPriceRepository.Table.FirstOrDefault(x =>
                    x.CustomerId == customerId && x.ProductId == productId));
            return pp == null ? (null, false) : (pp, true);
        });

        if (!productPrice.Item2)
            return null;
        return productPrice.pp.Price;
    }

    /// <summary>
    ///     Inserts a customer product price
    /// </summary>
    /// <param name="customerProductPrice">Customer product price</param>
    public virtual async Task InsertCustomerProductPrice(CustomerProductPrice customerProductPrice)
    {
        ArgumentNullException.ThrowIfNull(customerProductPrice);

        await _customerProductPriceRepository.InsertAsync(customerProductPrice);

        //clear cache
        await _cacheBase.RemoveAsync(string.Format(CacheKey.CUSTOMER_PRODUCT_PRICE_KEY_ID,
            customerProductPrice.CustomerId, customerProductPrice.ProductId));

        //event notification
        await _mediator.EntityInserted(customerProductPrice);
    }

    /// <summary>
    ///     Updates the customer product price
    /// </summary>
    /// <param name="customerProductPrice">Customer product price</param>
    public virtual async Task UpdateCustomerProductPrice(CustomerProductPrice customerProductPrice)
    {
        ArgumentNullException.ThrowIfNull(customerProductPrice);

        await _customerProductPriceRepository.UpdateAsync(customerProductPrice);

        //clear cache
        await _cacheBase.RemoveAsync(string.Format(CacheKey.CUSTOMER_PRODUCT_PRICE_KEY_ID,
            customerProductPrice.CustomerId, customerProductPrice.ProductId));

        //event notification
        await _mediator.EntityUpdated(customerProductPrice);
    }

    /// <summary>
    ///     Delete a customer product price
    /// </summary>
    /// <param name="customerProductPrice">Customer product price</param>
    public virtual async Task DeleteCustomerProductPrice(CustomerProductPrice customerProductPrice)
    {
        ArgumentNullException.ThrowIfNull(customerProductPrice);

        await _customerProductPriceRepository.DeleteAsync(customerProductPrice);

        //clear cache
        await _cacheBase.RemoveAsync(string.Format(CacheKey.CUSTOMER_PRODUCT_PRICE_KEY_ID,
            customerProductPrice.CustomerId, customerProductPrice.ProductId));

        //event notification
        await _mediator.EntityDeleted(customerProductPrice);
    }

    public virtual async Task<IPagedList<CustomerProductPrice>> GetProductsPriceByCustomer(string customerId,
        int pageIndex = 0, int pageSize = int.MaxValue)
    {
        var query = from pp in _customerProductPriceRepository.Table
            where pp.CustomerId == customerId
            select pp;
        return await PagedList<CustomerProductPrice>.Create(query, pageIndex, pageSize);
    }

    #endregion

    #region Personalize products

    /// <summary>
    ///     Gets a customer product
    /// </summary>
    /// <param name="id">Identifier</param>
    /// <returns>Customer product</returns>
    public virtual async Task<CustomerProduct> GetCustomerProduct(string id)
    {
        return await _customerProductRepository.GetByIdAsync(id);
    }

    /// <summary>
    ///     Gets a customer product
    /// </summary>
    /// <param name="customerId">Customer Identifier</param>
    /// <param name="productId">Product Identifier</param>
    /// <returns>Customer product</returns>
    public virtual async Task<CustomerProduct> GetCustomerProduct(string customerId, string productId)
    {
        var query = from pp in _customerProductRepository.Table
            where pp.CustomerId == customerId && pp.ProductId == productId
            select pp;

        return await Task.FromResult(query.FirstOrDefault());
    }

    /// <summary>
    ///     Insert a customer product
    /// </summary>
    /// <param name="customerProduct">Customer product</param>
    public virtual async Task InsertCustomerProduct(CustomerProduct customerProduct)
    {
        ArgumentNullException.ThrowIfNull(customerProduct);

        await _customerProductRepository.InsertAsync(customerProduct);

        //clear cache
        await _cacheBase.RemoveAsync(string.Format(CacheKey.PRODUCTS_CUSTOMER_PERSONAL_KEY,
            customerProduct.CustomerId));

        //event notification
        await _mediator.EntityInserted(customerProduct);
    }

    /// <summary>
    ///     Updates the customer product
    /// </summary>
    /// <param name="customerProduct">Customer product </param>
    public virtual async Task UpdateCustomerProduct(CustomerProduct customerProduct)
    {
        ArgumentNullException.ThrowIfNull(customerProduct);

        await _customerProductRepository.UpdateAsync(customerProduct);

        //clear cache
        await _cacheBase.RemoveAsync(string.Format(CacheKey.PRODUCTS_CUSTOMER_PERSONAL_KEY,
            customerProduct.CustomerId));

        //event notification
        await _mediator.EntityUpdated(customerProduct);
    }

    /// <summary>
    ///     Delete a customer product
    /// </summary>
    /// <param name="customerProduct">Customer product</param>
    public virtual async Task DeleteCustomerProduct(CustomerProduct customerProduct)
    {
        ArgumentNullException.ThrowIfNull(customerProduct);

        await _customerProductRepository.DeleteAsync(customerProduct);

        //clear cache
        await _cacheBase.RemoveAsync(string.Format(CacheKey.PRODUCTS_CUSTOMER_PERSONAL_KEY,
            customerProduct.CustomerId));

        //event notification
        await _mediator.EntityDeleted(customerProduct);
    }

    public virtual async Task<IPagedList<CustomerProduct>> GetProductsByCustomer(string customerId, int pageIndex = 0,
        int pageSize = int.MaxValue)
    {
        var query = from pp in _customerProductRepository.Table
            where pp.CustomerId == customerId
            orderby pp.DisplayOrder
            select pp;
        return await PagedList<CustomerProduct>.Create(query, pageIndex, pageSize);
    }

    #endregion
}